package info.opencards.core;

import info.opencards.Utils;
import info.opencards.ui.AbstractLearnDialog;
import info.opencards.ui.preferences.GeneralSettings;

import java.util.ArrayList;
import java.util.List;


/**
 * An abstract learning method.
 *
 * @author Holger Brandl
 */
public abstract class LearnMethod implements ScoringListener {

    // here we define constants for the three possible skipping behaviors, which can be configured in the OC-Settings
    public static final int SKIP_BUT_KEEP = 778800;
    public static final int SKIP_UNTIL_NEXT = 778801;
    public static final int SKIP_UNTIL_TOMORROW = 778802;

    protected final ItemValuater valuater;

    protected final List<LearnMethodListener> procListeners = new ArrayList<LearnMethodListener>();


    protected LearnMethod(ItemValuater itemValuater) {
        valuater = itemValuater;

        valuater.addScoringListener(this);
    }


    /**
     * @param parentCollection   The collection of items to which the set of scheduled items belongs to.
     * @param scheduledFileItems A set of items scheduled for today.
     * @return <code>true</code> if the card session has finshed without being interrupted.
     */
    public abstract void run(ItemCollection parentCollection, List<Item> scheduledFileItems);


    protected abstract void scoreNextItem();


    /**
     * Processes the feedback generated by the user for a given <code>item</code> . Beside the possible
     * learning-feedback-values which are defined in  <code>AbstractLearnDialog</code> there are some special
     * signal-feedback which needs to be processed: <ul> <li>SKIP_UNTIL_NEXT which should defer the <code>item</code> to
     * the next session <li>SKIP_UNTIL_TOMORROW which should defer the <code>item</code>  until tomorrow (if possible)
     * <li>ITEM_DELETED which indicates that the <code>item</code> is no longer contained in the item-collection </ul>
     *
     * @param item     The item which was scored
     * @param feedback The feedback generated by the user
     * @see info.opencards.ui.AbstractLearnDialog
     */
    protected abstract boolean processItemFeedBack(Item item, Integer feedback);


    public abstract void fireStatusInfo();


    public void itemScored(Item item, Integer feedback) {
        Utils.log("item '" + item.toString() + "'scored :" + feedback);

        if (feedback.equals(AbstractLearnDialog.SESSION_INTERRUPTED)) {
            for (LearnMethodListener procListener : procListeners) {
                procListener.cardFileProcessingFinished(true);
            }

            valuater.removeScoringListener(this);
            return;
        }

        // map (if "skip" was chosen by the user) the skip-feedback to the chosen skip-behavior
        if (feedback.equals(AbstractLearnDialog.SKIP_ITEM)) {
            feedback = Utils.getPrefs().getInt(GeneralSettings.SKIPPING_BEHAVIOR, GeneralSettings.SKIPPING_BEHAVIOR_DEFAULT);
        }

        if (!feedback.equals(SKIP_BUT_KEEP)) {
            boolean isStillOnSchedule = processItemFeedBack(item, feedback);

            for (LearnMethodListener procListener : procListeners)
                procListener.itemChanged(item, isStillOnSchedule, feedback);
            fireStatusInfo();
        }

        if (isFinished()) {
            for (LearnMethodListener procListener : procListeners) {
                procListener.cardFileProcessingFinished(false);
            }

            valuater.removeScoringListener(this);
        } else {
            scoreNextItem();
        }
    }


    protected abstract boolean isFinished();


    /**
     * Adds a new listener.
     */
    public void addLearnProcessListener(LearnMethodListener l) {
        if (l == null || procListeners.contains(l))
            return;

        procListeners.add(l);
    }


    /**
     * Removes a listener.
     */
    public void removeLearnProcessListener(LearnMethodListener l) {
        if (l == null)
            return;

        procListeners.remove(l);
    }
}
